Claude
Skills
Sign in
Back

sip-authentication-security

Included with Lifetime
$97 forever

Use when implementing SIP authentication, security mechanisms, and encryption. Use when securing SIP servers, clients, or proxies.

Security

What this skill does


# SIP Authentication and Security

Master SIP authentication mechanisms (HTTP Digest), TLS encryption, SIPS,
and security best practices for building secure VoIP applications.

## HTTP Digest Authentication

### Challenge-Response Flow

```
Client                                    Server
  |                                         |
  | REGISTER (no credentials)               |
  |---------------------------------------->|
  |                                         |
  |    401 Unauthorized                     |
  |    WWW-Authenticate: Digest             |
  |      realm="atlanta.com"                |
  |      nonce="dcd98b7102dd..."            |
  |      algorithm=MD5                      |
  |      qop="auth"                         |
  |<----------------------------------------|
  |                                         |
  | REGISTER (with Authorization)           |
  |    Authorization: Digest                |
  |      username="alice"                   |
  |      realm="atlanta.com"                |
  |      nonce="dcd98b7102dd..."            |
  |      uri="sip:atlanta.com"              |
  |      response="6629fae49393..."         |
  |      algorithm=MD5                      |
  |      qop=auth                           |
  |      nc=00000001                        |
  |      cnonce="0a4f113b"                  |
  |---------------------------------------->|
  |                                         |
  |    200 OK                               |
  |<----------------------------------------|
  |                                         |
```

### Digest Authentication Implementation

```typescript
import crypto from 'crypto';

interface DigestChallenge {
  realm: string;
  nonce: string;
  algorithm: 'MD5' | 'SHA-256';
  qop?: 'auth' | 'auth-int';
  opaque?: string;
  stale?: boolean;
}

interface DigestCredentials {
  username: string;
  realm: string;
  nonce: string;
  uri: string;
  response: string;
  algorithm: 'MD5' | 'SHA-256';
  cnonce?: string;
  nc?: string;
  qop?: string;
  opaque?: string;
}

class SipDigestAuth {
  // Generate authentication challenge (401/407 response)
  static generateChallenge(realm: string): DigestChallenge {
    return {
      realm,
      nonce: this.generateNonce(),
      algorithm: 'MD5',
      qop: 'auth',
      opaque: this.generateOpaque()
    };
  }

  // Create WWW-Authenticate or Proxy-Authenticate header
  static createChallengeHeader(challenge: DigestChallenge): string {
    let header = `Digest realm="${challenge.realm}", ` +
                 `nonce="${challenge.nonce}", ` +
                 `algorithm=${challenge.algorithm}`;

    if (challenge.qop) {
      header += `, qop="${challenge.qop}"`;
    }

    if (challenge.opaque) {
      header += `, opaque="${challenge.opaque}"`;
    }

    if (challenge.stale) {
      header += `, stale=TRUE`;
    }

    return header;
  }

  // Calculate response for authentication
  static calculateResponse(params: {
    username: string;
    password: string;
    realm: string;
    method: string;
    uri: string;
    nonce: string;
    algorithm?: 'MD5' | 'SHA-256';
    cnonce?: string;
    nc?: string;
    qop?: string;
    body?: string;
  }): string {
    const algorithm = params.algorithm || 'MD5';
    const hashFunc = algorithm === 'MD5' ? 'md5' : 'sha256';

    // Calculate A1 = MD5(username:realm:password)
    const a1 = this.hash(
      hashFunc,
      `${params.username}:${params.realm}:${params.password}`
    );

    // Calculate A2
    let a2: string;
    if (params.qop === 'auth-int') {
      // A2 = MD5(method:uri:MD5(body))
      const bodyHash = this.hash(hashFunc, params.body || '');
      a2 = this.hash(hashFunc, `${params.method}:${params.uri}:${bodyHash}`);
    } else {
      // A2 = MD5(method:uri)
      a2 = this.hash(hashFunc, `${params.method}:${params.uri}`);
    }

    // Calculate response
    let response: string;
    if (params.qop) {
      // response = MD5(A1:nonce:nc:cnonce:qop:A2)
      response = this.hash(
        hashFunc,
        `${a1}:${params.nonce}:${params.nc}:${params.cnonce}:${params.qop}:${a2}`
      );
    } else {
      // response = MD5(A1:nonce:A2)
      response = this.hash(hashFunc, `${a1}:${params.nonce}:${a2}`);
    }

    return response;
  }

  // Create Authorization or Proxy-Authorization header
  static createAuthorizationHeader(params: {
    username: string;
    password: string;
    realm: string;
    method: string;
    uri: string;
    nonce: string;
    algorithm?: 'MD5' | 'SHA-256';
    qop?: string;
    opaque?: string;
  }): string {
    const algorithm = params.algorithm || 'MD5';
    const cnonce = this.generateCnonce();
    const nc = '00000001';
    const qop = params.qop || 'auth';

    const response = this.calculateResponse({
      username: params.username,
      password: params.password,
      realm: params.realm,
      method: params.method,
      uri: params.uri,
      nonce: params.nonce,
      algorithm,
      cnonce,
      nc,
      qop
    });

    let header = `Digest username="${params.username}", ` +
                 `realm="${params.realm}", ` +
                 `nonce="${params.nonce}", ` +
                 `uri="${params.uri}", ` +
                 `response="${response}", ` +
                 `algorithm=${algorithm}`;

    if (qop) {
      header += `, qop=${qop}, nc=${nc}, cnonce="${cnonce}"`;
    }

    if (params.opaque) {
      header += `, opaque="${params.opaque}"`;
    }

    return header;
  }

  // Verify client credentials
  static verifyCredentials(
    credentials: DigestCredentials,
    password: string,
    method: string
  ): boolean {
    const expectedResponse = this.calculateResponse({
      username: credentials.username,
      password,
      realm: credentials.realm,
      method,
      uri: credentials.uri,
      nonce: credentials.nonce,
      algorithm: credentials.algorithm,
      cnonce: credentials.cnonce,
      nc: credentials.nc,
      qop: credentials.qop
    });

    return credentials.response === expectedResponse;
  }

  // Parse Authorization/Proxy-Authorization header
  static parseAuthorizationHeader(header: string): DigestCredentials | null {
    if (!header.startsWith('Digest ')) {
      return null;
    }

    const params: any = {};
    const paramRegex = /(\w+)=(?:"([^"]+)"|([^,\s]+))/g;
    let match;

    while ((match = paramRegex.exec(header)) !== null) {
      const key = match[1];
      const value = match[2] || match[3];
      params[key] = value;
    }

    return {
      username: params.username,
      realm: params.realm,
      nonce: params.nonce,
      uri: params.uri,
      response: params.response,
      algorithm: params.algorithm || 'MD5',
      cnonce: params.cnonce,
      nc: params.nc,
      qop: params.qop,
      opaque: params.opaque
    };
  }

  private static hash(algorithm: string, data: string): string {
    return crypto.createHash(algorithm).update(data).digest('hex');
  }

  private static generateNonce(): string {
    // Nonce = Base64(timestamp:ETag:private-key)
    const timestamp = Date.now();
    const etag = crypto.randomBytes(16).toString('hex');
    const privateKey = 'secret-server-key';
    const nonce = `${timestamp}:${etag}:${privateKey}`;
    return Buffer.from(nonce).toString('base64');
  }

  private static generateCnonce(): string {
    return crypto.randomBytes(16).toString('hex');
  }

  private static generateOpaque(): string {
    return crypto.randomBytes(16).toString('hex');
  }
}
```

### Complete Authentication Example

```typescript
class SipAuthenticatedClient {
  private username: string;
  private password: string;
  private realm?: string;
  private nonce?: string;
  private opaque?: string;

  constructor(username: string, password: string) {
    this.username = username;
    this.password = password;
  }

  // Send REGISTER with authentication
  async register(server: string): Promise<void> {
    // First attempt without credentials
    let response = await th

Related in Security